package cc.git.liuyan.core;

import cc.git.liuyan.core.listen.WebElementListen;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.edge.EdgeOptions;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.time.Duration;
import java.util.*;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class GlobalEdgeWebDriver {
    private static boolean initialized = false;
    private static WebDriver driver;
    private static WebDriverWait defaultWait;
    private static String mainWindowHandle;
    private static Map<String, String> name2WindowHandle = new HashMap<String, String>();
    private static ScheduledExecutorService webElementListenExecutor = null;

    public static void initWebDriver(String edgeDriverPath, boolean headless) {
        if (initialized) {
            throw new RuntimeException("WebDriver has already been initialized");
        }
        initialized = true;
        System.setProperty("webdriver.edge.driver", edgeDriverPath);
        EdgeOptions edgeOptions = new EdgeOptions();
        if (headless) {
            edgeOptions.addArguments("--headless");//无头模式
        }
        edgeOptions.addArguments("--remote-allow-origins=*");
        driver = new EdgeDriver(edgeOptions);
        defaultWait = new WebDriverWait(driver, Duration.ofSeconds(30));
        webElementListenExecutor = new ScheduledThreadPoolExecutor(10);
    }

    public static void setWebElementListenExecutor(int size) {
        if (size > 10) {
            webElementListenExecutor = new ScheduledThreadPoolExecutor(size);
        }
    }

    public static void open(String url) {
        driver.get(url);
        mainWindowHandle = driver.getWindowHandle();
    }

    public static void quit() {
        driver.quit();
        driver = null;
        defaultWait = null;
        mainWindowHandle = null;
        name2WindowHandle.clear();
        initialized = false;
        webElementListenExecutor.shutdown();
        webElementListenExecutor = null;
    }

    public static void openNewWindowHandle(String url) {
        JavascriptExecutor js = (JavascriptExecutor) driver;
        js.executeScript("window.open('" + url + "', '_blank');");
        //
        Set<String> windowHandles = driver.getWindowHandles();
        driver.switchTo().window(windowHandles.toArray()[windowHandles.size() - 1].toString());
    }

    public static void openNewWindowHandleWithName(String url, String name) {
        JavascriptExecutor js = (JavascriptExecutor) driver;
        js.executeScript("window.open('" + url + "', '_blank');");
        //
        Set<String> windowHandles = driver.getWindowHandles();
        driver.switchTo().window(windowHandles.toArray()[windowHandles.size() - 1].toString());
        name2WindowHandle.put(name, driver.getWindowHandle());
    }

    public static void switchWindowHandleByName(String name) {
        driver.switchTo().window(name2WindowHandle.get(name));
    }

    public static void switchMainWindowHandle() {
        driver.switchTo().window(mainWindowHandle);
    }

    public static void closeNowWindowHandle() {
        driver.close();
        //
        if (driver != null) {
            Set<String> windowHandles = driver.getWindowHandles();
            if (windowHandles != null && !windowHandles.isEmpty()) {
                driver.switchTo().window(windowHandles.toArray()[windowHandles.size() - 1].toString());
            }
        }
    }

    public static void closeOtherWindowHandle() {
        Set<String> windowHandles = driver.getWindowHandles();
        for (String windowHandle : windowHandles) {
            if (!Objects.equals(mainWindowHandle, windowHandle)) {
                driver.switchTo().window(windowHandle);
                closeNowWindowHandle();
            } else {
                continue;
            }
        }
        switchMainWindowHandle();
    }

    public static void printNowWindowHandle() {
        System.out.println("当前标签页如下：");
        System.out.println(driver.getTitle());
        System.out.println(driver.getCurrentUrl());
        System.out.println(driver.getWindowHandle());
        System.out.println(String.join(",", driver.getWindowHandles()));
    }

    public static WebElement findElement(String xpath) {
        return defaultWait.until(ExpectedConditions.presenceOfElementLocated(By.xpath(xpath)));
    }

    public static WebElement findElement(WebElement parent, String xpath) {
        return defaultWait.until(ExpectedConditions.presenceOfNestedElementLocatedBy(parent, By.xpath(xpath)));
    }

    public static List<WebElement> findElementList(String xpath) {
        return defaultWait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(By.xpath(xpath)));
    }

    public static List<WebElement> findElementList(WebElement parent, String xpath) {
        return defaultWait.until(new ExpectedCondition<List<WebElement>>() {
            public List<WebElement> apply(WebDriver webDriver) {
                return parent.findElements(By.xpath(xpath));
            }

            public String toString() {
                return String.format("visibility of element located by %s", xpath);
            }
        });
    }

    public static void switchFrame(WebElement frameWebElement) {
        driver.switchTo().frame(frameWebElement);
    }

    public static void scheduleListen(WebElement element, Object initData, WebElementListen webElementListen, int scheduleSecond) {
        ScheduledFuture<?> scheduledFuture = webElementListenExecutor.scheduleWithFixedDelay(() -> {
            webElementListen.listen(element, initData);
        }, 0, scheduleSecond, TimeUnit.SECONDS);
    }
}
